#include "Socket.hpp"
#include <sys/select.h>

using namespace sock_ns;

const static int N = sizeof(fd_set) * 8;//位图的字节大小*8-->bit大小
const static int defaultfd = -1;

class SelectServer
{
    public:

    //构造函数
    SelectServer(int16_t port)
        :_listensock(std::make_unique<TcpSocket>())
    {
        InetAddr addr("0",port);
        _listensock->BuildListenSocket(addr);
        //初始化一下辅助数组
        for(int i=0;i<N;i++)
        {
            _auxiliary_arr[i] = defaultfd;
        }
        //把listenfd放到数组中
        _auxiliary_arr[0] = _listensock->SockFd();
    }

    ~SelectServer()
    {
    }

    std::string RfdsToString()
    {
        std::string fdstr;
        for (int i = 0; i < N; i++)
        {
            if (_auxiliary_arr[i] == defaultfd)
                continue;
            fdstr += std::to_string(_auxiliary_arr[i]);
            fdstr += " ";
        }
        return fdstr;
    }


    void AcceptClient()
    {
        //接受一下新的fd
        InetAddr clientaddr;
        int sockfd = _listensock->Accepter(&clientaddr);

        //把fd加载到辅助数组中
        int i = 0;
        for(i=0;i<N;i++)
        {   
            if(_auxiliary_arr[i]==defaultfd)
                break;
        }
        if(i==N)
        {
            close(sockfd);//关闭fd
            //说明辅助数组满了
            Log(WARNING,"server is full");
            return;
        }
        else
        {
            _auxiliary_arr[i] = sockfd;
            Log(DEBUG,"%d add array!",_auxiliary_arr[i]);
        }
        //打印一下当前辅助数组中的fd
        Log(DEBUG, "curr fd_array[] fd list : %s\n", RfdsToString().c_str());
    }
    

    void ServerIo(int pos)
    {
        int sockfd = _auxiliary_arr[pos];
        char buffer[1024];
        int n = read(sockfd,buffer,sizeof(buffer));
        if(n>0)
        {
            buffer[n] = 0;
            std::cout<<"client say#:"<<buffer;
            std::string serverstr = "server echo#:";
            serverstr += buffer;
            send(sockfd,serverstr.c_str(),serverstr.size(),0);
        }
        else if(n==0)
        {
            //说明客户端的写端已经关闭
            Log(DEBUG, "%d is closed\n", _auxiliary_arr[pos]);
            //把fd关闭，把辅助数组还原
            ::close(_auxiliary_arr[pos]);
            _auxiliary_arr[pos] = defaultfd;
            Log(DEBUG, "curr fd_array[] fd list : %s\n", RfdsToString().c_str());
        }
        else//读取出错了
        {
            Log(DEBUG, "%d recv error\n", _auxiliary_arr[pos]);
            //把fd关闭，把辅助数组还原
            ::close(_auxiliary_arr[pos]);
            _auxiliary_arr[pos] = defaultfd;
            Log(DEBUG, "curr fd_array[] fd list : %s\n", RfdsToString().c_str());
        }
    }

    void HandlerEvent(fd_set& rfds)
    {
        //遍历一下辅助数组
        for(int i=0;i<N;i++)
        {
            if(_auxiliary_arr[i]==defaultfd)
                continue;
            else//检查一下有没有命中的
            {
                //如果命中了，判断是不是listenfd?
                if(FD_ISSET(_auxiliary_arr[i],&rfds))
                {
                    if(_auxiliary_arr[i]==_listensock->SockFd())
                    {
                        AcceptClient();
                    }
                    else
                    {
                        ServerIo(i);
                    }
                }
            }
        }
    }

    //循环函数
    void Loop()
    {
        while(1)
        {
            //每次使用select函数，参数都要重新设置
            // rfds是一个输入输出型参数，每次调用，都要对rfds进行重新设定!
            fd_set rfds;
            FD_ZERO(&rfds);
            int maxfd =0;
            for(int i=0;i<N;i++)
            {
                if(_auxiliary_arr[i]==defaultfd)
                    continue;
                //需要添加的，而且需要找出最大fd
                FD_SET(_auxiliary_arr[i],&rfds);
                maxfd = _auxiliary_arr[i]>maxfd?_auxiliary_arr[i]:maxfd;

            }           
            struct timeval timeout = {1,0}; //等待时间为1秒

            int n = select(maxfd+1,&rfds,nullptr,nullptr,&timeout);
            switch(n)
            {
            case 0://返回值为0，表示超时了
                Log(INFO, "timeout, %d.%d", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1://返回值为负数，表示出错了
                Log(ERROR, "select error...");
                break;
            default:
                HandlerEvent(rfds);//处理事件
                //Log(DEBUG,"Handler...");
                break;
            }
        }
    }


    private:
    std::unique_ptr<Socket> _listensock;

    int _auxiliary_arr[N];//辅助数组

};